﻿// FLMScanViewModel
// 
// ViewModel (MVVM pattern) of FLMScan application 
// 
// FLMScan - Featured by FabLab Munich

using System;
using System.Drawing;
using System.ComponentModel;
using log4net;
using System.Windows.Input;
using FLMScan.Commands;

namespace FLMScan
{
    class FLMScanViewModel : INotifyPropertyChanged, IDisposable
    {
        #region Events
        public event PropertyChangedEventHandler PropertyChanged;
        private event EventHandler RequestCloseEvent;

        private event EventHandler SaveRequested;
        private event EventHandler StartScanRequested;
        private event EventHandler StopScanRequested;
        private event EventHandler CalibrationRequested;
        #endregion

        #region Fields

        private ILog logger;
        private bool isDisposed = false;        
        
        // device states
        private DeviceState laserPowerState;
        private DeviceState stepperPowerState;
        private DeviceState lightPowerState;
        private DeviceState webcamConnectionState;
        private DeviceState arduinoConnectionState;
        private DeviceState doorContactState;

        // UI selections
        
        // export settings
        private bool imageExportState;
        private bool geometryExportState;
        private bool geometryExportStateStl;
        private bool geometryExportStatePly;
        private string fileName;
        
        // scan quality settings
        private bool draftScanState;
        private bool finalScanState;
        private int draftScanSteps;
        private int finalScanSteps;

        // enabled state of scan quality controls
        private bool draftScanEnabled;
        private bool finalScanEnabled;
        private bool draftScanStepsEnabled;
        private bool finalScanStepsEnabled;
        
        // scan progress
        private int currentScanStep;
        private int totalScanSteps;
        private string remainingTime;

        // visual feedback
        private Bitmap scanImage;
        private string statusMessage;        

        // commands
        private CalibrateCommand calibrateCommand;
        private EditPreferencesCommand editPreferencesCommand;
        private PreviewScanCommand previewScanCommand;
        private SaveCommand saveCommand;
        private SignalDoorContactOpenCommand signalDoorContactOpenCommand;
        private StartScanCommand startScanCommand;
        private StopScanCommand stopScanCommand;

        // TBD: add these fields if required
        // 3D preview "put object on table"        
        // Scan progress bar (redundant to CurrentScanStep)                

        #endregion

        public FLMScanViewModel()
        {
            logger = LogManager.GetLogger(typeof(FLMScanViewModel));            
            logger.Debug("Constructing FLMScanViewModel.");            

            Initialize();
        }

        #region Properties

        public DeviceState LaserPowerState
        {
            get
            {
                return laserPowerState;
            }
            set
            {
                logger.Debug("LaserPowerState set to " + value);
                laserPowerState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("LaserPowerState"));
                }
            }
        }

        public DeviceState StepperPowerState
        {
            get
            {
                return stepperPowerState;
            }
            set
            {
                logger.Debug("StepperPowerState set to " + value);
                stepperPowerState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("StepperPowerState"));
                }
            }
        }

        public DeviceState LightPowerState
        {
            get
            {
                return lightPowerState;
            }
            set
            {
                logger.Debug("LightPowerState set to " + value);
                lightPowerState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("LightPowerState"));
                }
            }
        }

        public DeviceState WebcamConnectionState
        {
            get
            {
                return webcamConnectionState;
            }
            set
            {
                logger.Debug("WebcamConnectionState set to " + value);
                webcamConnectionState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("WebcamConnectionState"));
                }
            }
        }

        public DeviceState ArduinoConnectionState
        {
            get
            {
                return arduinoConnectionState;
            }
            set
            {
                logger.Debug("ArduinoConnectionState set to " + value);
                arduinoConnectionState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("ArduinoConnectionState"));
                }
            }
        }

        public DeviceState DoorContactState
        {
            get
            {
                return doorContactState;
            }
            set
            {
                logger.Debug("DoorContactState set to " + value);
                doorContactState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("DoorContactState"));
                }
            }
        }            

        public bool ImageExportState
        {
            get 
            {                 
                return imageExportState; 
            }
            set
            {
                logger.Debug("ImageExportState set to " + value);
                imageExportState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("ImageExportState"));
                }
            }
        }

        public bool GeometryExportState
        {
            get 
            { 
                return geometryExportState; 
            }
            set
            {
                logger.Debug("GeometryExportState set to " + value);
                geometryExportState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("GeometryExportState"));
                }
            }
        }

        public bool GeometryExportStateStl
        {
            get
            {
                return geometryExportStateStl;
            }
            set
            {
                logger.Debug("GeometryExportStateStl set to " + value);
                geometryExportStateStl = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("GeometryExportStateStl"));
                }
            }
        }

        public bool GeometryExportStatePly
        {
            get
            {
                return geometryExportStatePly;
            }
            set
            {
                logger.Debug("GeometryExportStatePly set to " + value);
                geometryExportStatePly = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("GeometryExportStatePly"));
                }
            }
        }

        public bool DraftScanState
        {
            get
            {
                return draftScanState;
            }
            set
            {
                logger.Debug("DraftScanState set to " + value);
                draftScanState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("DraftScanState"));
                }
            }
        }

        public bool FinalScanState
        {
            get
            {
                return finalScanState;
            }
            set
            {
                logger.Debug("FinalScanState set to " + value);
                finalScanState = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FinalScanState"));
                }
            }
        }

        public int DraftScanSteps
        {
            get
            {
                return draftScanSteps;
            }
            set
            {
                logger.Debug("DraftScanSteps set to " + value);
                draftScanSteps = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("DraftScanSteps"));
                }
            }
        }

        public int FinalScanSteps
        {
            get
            {
                return finalScanSteps;
            }
            set
            {
                logger.Debug("FinalScanSteps set to " + value);
                finalScanSteps = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FinalScanSteps"));
                }
            }
        }


        public bool DraftScanEnabled
        {
            get
            {
                return draftScanEnabled;
            }
            set
            {
                logger.Debug("DraftScanEnabled set to " + value);
                draftScanEnabled = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("DraftScanEnabled"));
                }
            }
        }

        public bool FinalScanEnabled
        {
            get
            {
                return finalScanEnabled;
            }
            set
            {
                logger.Debug("FinalScanEnabled set to " + value);
                finalScanEnabled = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FinalScanEnabled"));
                }
            }
        }

        public bool DraftScanStepsEnabled
        {
            get
            {
                return draftScanStepsEnabled;
            }
            set
            {
                logger.Debug("DraftScanStepsEnabled set to " + value);
                draftScanStepsEnabled = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("DraftScanStepsEnabled"));
                }
            }
        }

        public bool FinalScanStepsEnabled
        {
            get
            {
                return finalScanStepsEnabled;
            }
            set
            {
                logger.Debug("FinalScanStepsEnabled set to " + value);
                finalScanStepsEnabled = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FinalScanStepsEnabled"));
                }
            }
        }

        public int CurrentScanStep
        {
            get
            {
                return currentScanStep;
            }
            set
            {
                logger.Debug("CurrentScanStep set to " + value);
                currentScanStep = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("CurrentScanStep"));
                }
            }
        }

        public int TotalScanSteps
        {
            get
            {
                return totalScanSteps;
            }
            set
            {
                logger.Debug("TotalScanSteps set to " + value);
                totalScanSteps = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("TotalScanSteps"));
                }
            }
        }

        public string RemainingTime
        {
            get
            {
                return remainingTime;
            }
            set
            {
                logger.Debug("RemainingTime set to " + value);
                remainingTime = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("RemainingTime"));
                }
            }
        }

        public string StatusMessage
        {
            get
            {
                return statusMessage;
            }
            set
            {
                logger.Debug("StatusMessage set to " + value);
                statusMessage = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("StatusMessage"));
                }
            }
        }

        public string FileName
        {
            get
            {
                return fileName;
            }
            set
            {
                logger.Debug("FileName set to " + value);
                fileName = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FileName"));
                }
            }
        }
        #endregion

        #region Commands

        // Required UI commands

        public ICommand CalibrateCommand
        {
            get { return calibrateCommand; }
        }

        public ICommand EditPreferencesCommand
        {
            get { return editPreferencesCommand; }
        }

        public ICommand PreviewScanCommand
        {
            get { return previewScanCommand; }
        }

        public ICommand SaveCommand
        {
            get { return saveCommand; }
        }

        public ICommand SignalDoorContactOpenCommand
        {
            get { return signalDoorContactOpenCommand; }
        }

        public ICommand StartScanCommand
        {
            get { return startScanCommand; }
        }

        public ICommand StopScanCommand
        {
            get { return stopScanCommand; }
        }

        #endregion

        #region CommandHandlers

        public void OnCalibrateCommand()
        {
            logger.Debug("CalibrateCommand triggered.");

            if (CalibrationRequested != null)
            {
                CalibrationRequested(this, EventArgs.Empty);
            }
        }

        public void OnEditPreferencesCommand()
        {
            logger.Debug("EditPreferencesCommand triggered.");
        }

        public void OnPreviewScanCommand()
        {
            logger.Debug("PreviewScanCommand triggered.");                        
        }

        public void OnSaveCommand()
        {
            logger.Debug("SaveCommand triggered.");

            if (SaveRequested != null)
            {
                SaveRequested(this, EventArgs.Empty);
            }
        }
        
        public void OnSignalDoorContactOpenCommand()
        {
            logger.Debug("SignalDoorContactOpenCommand triggered.");
        }

        public void OnStartScanRequested()
        {
            logger.Debug("StartScanCommand triggered.");

            if (StartScanRequested != null)
            {
                StartScanRequested(this, EventArgs.Empty);                
            }
        }

        public void OnStopScanCommand()
        {
            logger.Debug("StopScanCommand triggered.");

            if (StopScanRequested != null)
            {
                StopScanRequested(this, EventArgs.Empty);
            }
        }

        #endregion

        public void RequestClose()
        {
            if (RequestCloseEvent != null)
            {
                RequestCloseEvent(this, EventArgs.Empty);
            }
        }

        public event EventHandler OnRequestClose
        {
            add { RequestCloseEvent += value; }
            remove { RequestCloseEvent -= value; }
        }

        public event EventHandler OnRequestSave
        {
            add { SaveRequested += value; }
            remove { SaveRequested -= value; }
        }

        public event EventHandler OnRequestStartScan
        {
            add { StartScanRequested += value; }
            remove { StartScanRequested -= value; }
        }

        public event EventHandler OnRequestStopScan
        {
            add { StopScanRequested += value; }
            remove { StopScanRequested -= value; }
        }        

        public event EventHandler OnRequestCalibration
        {
            add { CalibrationRequested += value; }
            remove { CalibrationRequested -= value; }
        }
        
        #region Private Methods

        private void Initialize()
        {
            laserPowerState = DeviceState.eUnknown;
            stepperPowerState = DeviceState.eUnknown;
            lightPowerState = DeviceState.eUnknown;
            webcamConnectionState = DeviceState.eUnknown;
            arduinoConnectionState = DeviceState.eUnknown;
            doorContactState = DeviceState.eUnknown;

            imageExportState = false;
            geometryExportState = true;
            geometryExportStateStl = true;
            geometryExportStatePly = false;
            fileName = String.Empty;
        
            draftScanState = true;
            finalScanState = false;
            draftScanSteps = 20;
            finalScanSteps = 100;

            draftScanEnabled = true;
            finalScanEnabled = true;
            draftScanStepsEnabled = true;
            finalScanStepsEnabled = true;
        
            currentScanStep = 1;
            totalScanSteps = 20;
            remainingTime = "1min 30sec";
            
            scanImage = null;
            statusMessage = String.Empty;

            StateMachine stateMachineInstance = StateMachine.Instance();

            calibrateCommand = new CalibrateCommand(this, stateMachineInstance);
            editPreferencesCommand = new EditPreferencesCommand(this, stateMachineInstance);
            previewScanCommand = new PreviewScanCommand(this, stateMachineInstance);
            saveCommand = new SaveCommand(this, stateMachineInstance);
            signalDoorContactOpenCommand = new SignalDoorContactOpenCommand(this, stateMachineInstance);
            startScanCommand = new StartScanCommand(this, stateMachineInstance);
            stopScanCommand = new StopScanCommand(this, stateMachineInstance);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool isDisposing)
        {
            if (isDisposed)
                return;

            if (isDisposing)
            {
                isDisposed = true;                
            }
        }

        #endregion
    }    
}
